import { fabric } from 'fabric';

import { getNoAnglePoints } from '@/pages/TemplateEditor/components/Editor/components/TemplateEffect/utils';

import { getNewLayerInfo } from '../layers';

export const getImageBoundByCropped = (
  cropBound:
    | {
        width: number;
        height: number;
        left: number;
        top: number;
        angle: number;
      }
    | fabric.Object,
  cropped: [number, number, number, number]
): { width: number; height: number; left: number; top: number } => {
  const width =
    'getScaledWidth' in cropBound
      ? cropBound.getScaledWidth()
      : cropBound.width;
  const height =
    'getScaledHeight' in cropBound
      ? cropBound.getScaledHeight()
      : cropBound.height;
  const { left, top, angle } = cropBound;
  const [topCropped, rightCropped, bottomCropped, leftCropped] = cropped;

  const imageWidth = (width * 100) / (100 - leftCropped - rightCropped);
  const imageHeight = (height * 100) / (100 - topCropped - bottomCropped);

  const imageLeft =
    left +
    (width * ((rightCropped - leftCropped) / 2)) /
      (100 - leftCropped - rightCropped);

  const imageTop =
    top +
    (height * ((bottomCropped - topCropped) / 2)) /
      (100 - topCropped - bottomCropped);

  const currentImageCenterPoint = getRotatedPositionByCustomPoint(
    new fabric.Point(left, top),
    new fabric.Point(imageLeft, imageTop),
    -angle
  );

  return {
    width: imageWidth,
    height: imageHeight,
    left: currentImageCenterPoint.x,
    top: currentImageCenterPoint.y
  };
};

export const getRotatedPositionByCustomPoint = (
  rotationOrigin,
  rotatePoint,
  angle
): fabric.Point => {
  const angleRadians = fabric.util.degreesToRadians(-angle);
  return fabric.util.rotatePoint(rotatePoint, rotationOrigin, angleRadians);
};

// 图片被裁剪百分之多少，用于后续计算，以及提交后端，输出为：[上,右,下,左]，相对图片本身比例 范围0-100
export const getImageCropped = (
  cropPath: fabric.Object,
  imgObject: fabric.Object
): [number, number, number, number] => {
  const imageWidth = imgObject.getScaledWidth();
  const imageHeight = imgObject.getScaledHeight();

  const cropPathPoints = getNoAnglePoints(cropPath);
  const imagePoints = getNoAnglePoints(imgObject);
  const diffCropPathCenterPoint = getRotatedPositionByCustomPoint(
    imagePoints.center,
    cropPathPoints.center,
    cropPath.angle
  );
  const diffCropPathPoints = getNoAnglePoints(
    cropPath,
    diffCropPathCenterPoint
  );

  const topCropped =
    ((diffCropPathPoints.leftTop.y - imagePoints.leftTop.y) / imageHeight) *
    100;

  const rightCropped =
    ((imagePoints.rightBottom.x - diffCropPathPoints.rightBottom.x) /
      imageWidth) *
    100;

  const bottomCropped =
    ((imagePoints.rightBottom.y - diffCropPathPoints.rightBottom.y) /
      imageHeight) *
    100;

  const leftCropped =
    ((diffCropPathPoints.leftTop.x - imagePoints.leftTop.x) / imageWidth) * 100;

  // 仅保留15位小数，同时避免出现科学计数法
  const fixed15 = (num: number): number => {
    // 当小于10的-6次方时，表示实际已经为0，只是旋转导致的精度误差而出现极小的数。
    if (num < 1e-6) {
      return 0;
    }

    return Number(num.toFixed(15));
  };

  return [
    fixed15(topCropped),
    fixed15(rightCropped),
    fixed15(bottomCropped),
    fixed15(leftCropped)
  ];
};

export const getCroppedImage = async (params: {
  cropPath;
  imgObject;
  canvas;
  canvasScale;
  otherObjects;
  callBack: (newImageJson, imageCropped) => Promise<void>;
}): Promise<void> => {
  const { cropPath, imgObject, callBack } = params;

  const imageCropped = getImageCropped(cropPath, imgObject);
  const topCropped = imageCropped[0];
  const leftCropped = imageCropped[3];

  const originOpacity = imgObject.opacity;

  // 先取消旋转 + 设置图片为可见状态，再截取图片。
  (imgObject as fabric.Image)
    .rotate(0)
    .set({ opacity: 1 })
    .cloneAsImage(
      (image: fabric.Image) => {
        const newImageJson = {
          ...getNewLayerInfo(cropPath),
          src: image.getSrc()
        };
        callBack(newImageJson, imageCropped);
      },
      {
        left: (imgObject.getScaledWidth() * leftCropped) / 100,
        top: (imgObject.getScaledHeight() * topCropped) / 100,
        width: cropPath.getScaledWidth(),
        height: cropPath.getScaledHeight()
      }
    )
    .rotate(cropPath.angle)
    .set({ opacity: originOpacity });
};
